﻿using Sustainalytics.Entities;
using Sustainalytics.Entities.Governance;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.Governance.Synchronization
{
    public static class CorporateGovernanceEntityConverter
    {
        public static List<GovernanceIndicatorTemplate> GetIndicatorTemplates(
            IList<SharedEntities.CorporateGovernanceResearch.GovernanceIndicatorTemplate> governanceIndicatorTemplates)
        {
            return governanceIndicatorTemplates.Select(x => new GovernanceIndicatorTemplate
            {
                Id = x.Id,
                Name = x.Name,
                Number = x.Number,
                SortKey = x.SortKey,
                IssueName = x.IssueName,
                IssueNumber = x.IssueNumber,
                AnswerCategories = x.AnswerCategories.Select(ac => new GovernanceAnswerCategory
                {
                    Code = string.Empty,
                    Score = ac.Score,
                    Text = ac.Text
                }).ToList(),
                Criteria = x.Criteria.Select(c => new GovernanceCriterion
                {
                    Number = c.Number,
                    Text = c.Text,
                    IsChecked = c.IsChecked
                }).ToList()
            })
            .ToList();
        }

        public static IList<GovernanceComputationResult> GetGovernanceComputationResults(
            IList<SharedEntities.CorporateGovernanceResearch.CorporateGovernanceResearchProfile> entities)
        {
            var governanceComputationResults = entities.Select(x => new GovernanceComputationResult
            {
                Id = x.Id,
                OverallPerformance = null,
                RelativePerformance = null,
                IssuesPerformance = GetIssuesPerformance(x.GovernanceComputationResult.IssuesPerformance)
            })
            .ToList();

            return governanceComputationResults;
        }

        private static List<GovernanceIssuePerformance> GetIssuesPerformance(
           IEnumerable<SharedEntities.CorporateGovernanceResearch.IssuePerformance> entities)
        {
            if (!entities.Any()) return null;

            return entities.Select(x => new GovernanceIssuePerformance
            {
                Name = x.Name,
                Number = x.Number,
                Assessment = (AssessmentType)x.Assessment.Value,
                Performances = new List<GovernancePerformance>(),
                Score = x.Score.Value,
                Weight = x.Weight.Value,
                WeightedScore = x.WeightedScore.Value
            })
           .ToList();
        }

        public static IList<GovernanceIssue> GetGovernanceIssues(
            IList<SharedEntities.CorporateGovernanceResearch.CorporateGovernanceResearchProfile> entities,
            Dictionary<Guid, Dictionary<string, double>> issuePerformanceDictionary,
            IDictionary<string, SharedEntities.CorporateGovernanceResearch.GovernanceIndicatorTemplate> governanceIndicatorTemplates)
        {
            return entities.SelectMany(x => GetGovernanceIssues(x, issuePerformanceDictionary[x.Id], governanceIndicatorTemplates)).ToList();
        }

        private static IList<GovernanceIssue> GetGovernanceIssues(
            SharedEntities.CorporateGovernanceResearch.CorporateGovernanceResearchProfile entity,
            Dictionary<string, double> issuePerformance,
            IDictionary<string, SharedEntities.CorporateGovernanceResearch.GovernanceIndicatorTemplate> governanceIndicatorTemplates)
        {
            return entity.GovernanceIssues.Select(x =>
            {
                governanceIndicatorTemplates.TryGetValue(x.Number, out var indicatorTemplate);
                issuePerformance.TryGetValue(x.Number, out var issueScore);

                return new GovernanceIssue
                {
                    Id = Guid.NewGuid(),
                    CompanyId = entity.Id,
                    Number = x.Number,
                    Name = indicatorTemplate == null ? x.Name : indicatorTemplate.Name,
                    Score = (int)Math.Round(issueScore, 0, MidpointRounding.AwayFromZero),
                    Outlook = x.Outlook == null
                       ? null
                       : (OutlookType?)x.Outlook.Value,
                    Indicators = GetIndicators(entity, x.Indicators, governanceIndicatorTemplates),
                    Comment = x.Comment?.Value
                };
            }).ToList();
        }

        public static IList<GovernanceSource> GetGovernanceSources(IList<GovernanceIssue> governanceIssues)
        {
            Dictionary<KeyValuePair<Guid, string>, GovernanceSource> companyTextIndicatorNumberSources =
                new Dictionary<KeyValuePair<Guid, string>, GovernanceSource>();

            foreach (var governanceIssue in governanceIssues)
            {
                var indicatorSources = new List<IndicatorSource>();

                foreach (var indicator in governanceIssue.Indicators)
                {
                    if (!indicator.Sources.Any()) continue;

                    foreach (var source in indicator.Sources)
                    {
                        var text = source;
                        var length = source.IndexOf("(I");
                        if (length > -1)
                        {
                            text = source.Substring(0, length - 1);
                        }

                        indicatorSources.Add(new IndicatorSource
                        {
                            Text = text,
                            IndicatorsNumber = indicator.Number
                        });
                    }
                }

                var sources = indicatorSources.GroupBy(x => x.Text).Select(g => new GovernanceSource
                {
                    Id = Guid.NewGuid(),
                    CompanyId = governanceIssue.CompanyId,
                    Text = g.Key,
                    IndicatorsNumbers = g.Select(i => i.IndicatorsNumber).ToList()
                }).ToList();

                sources.ForEach(source =>
                {
                    companyTextIndicatorNumberSources.TryGetValue(new KeyValuePair<Guid, string>(source.CompanyId, source.Text), out var governanceSource);

                    if (governanceSource == null)
                    {
                        companyTextIndicatorNumberSources.Add(new KeyValuePair<Guid, string>(source.CompanyId, source.Text), source);
                    }
                    else
                    {
                        governanceSource.IndicatorsNumbers.AddRange(source.IndicatorsNumbers);
                        governanceSource.IndicatorsNumbers = governanceSource.IndicatorsNumbers.Distinct().ToList();
                    }
                });
            }

            return companyTextIndicatorNumberSources.Select(x => x.Value).ToList();
        }

        public static IList<GovernanceAssessmentThreshold> GetAssessmentThresholds(
            IList<SharedEntities.CorporateGovernanceResearch.GovernanceAssessmentThreshold> assessmentThresholds)
        {
            return assessmentThresholds
                .OrderBy(x => x.Number)
                .Select(x => new GovernanceAssessmentThreshold()
                {
                    Id = x.Id,
                    Number = x.Number,
                    Thresholds = x.Thresholds
                })
                .ToList();
        }

        private static List<GovernanceIndicator> GetIndicators(
            SharedEntities.CorporateGovernanceResearch.CorporateGovernanceResearchProfile entity,
            IEnumerable<SharedEntities.CorporateGovernanceResearch.GovernanceIndicator> entities,
            IDictionary<string, SharedEntities.CorporateGovernanceResearch.GovernanceIndicatorTemplate> governanceIndicatorTemplates)
        {
            if (!entities.Any())
                return null;

            return entities.Select(x =>
            {
                governanceIndicatorTemplates.TryGetValue(x.Number, out var indicatorTemplate);

                return new GovernanceIndicator
                {
                    Id = Guid.NewGuid(),
                    CompanyId = entity.Id,
                    Number = x.Number,
                    Name = x.Name,
                    Description = (indicatorTemplate == null) ? x.Description : indicatorTemplate.Description,
                    Score = x.Score.Value,
                    Outlook = 0,
                    Sources = x.Sources.ToList(),
                    Tickedboxes = GetTickboxes(x),
                    Untickedboxes = GetUntickedboxes(x, governanceIndicatorTemplates),
                    Comment = x.Comment?.Value
                };
            })
           .ToList();
        }

        private static List<KeyValuePair<string, string>> GetTickboxes(
            SharedEntities.CorporateGovernanceResearch.GovernanceIndicator governanceIndicator)
        {
            if (governanceIndicator.IsPillar6())
            {
                return new List<KeyValuePair<string, string>>
                {
                    new KeyValuePair<string, string>("", governanceIndicator.AnswerCategory.Text)
                };
            }

            return GetTickedboxes(governanceIndicator.CheckBoxes, true);
        }

        private static List<KeyValuePair<string, string>> GetUntickedboxes(
            SharedEntities.CorporateGovernanceResearch.GovernanceIndicator governanceIndicator,
            IDictionary<string, SharedEntities.CorporateGovernanceResearch.GovernanceIndicatorTemplate> governanceIndicatorTemplates)
        {
            if (governanceIndicator.IsPillar6())
            {
                governanceIndicatorTemplates.TryGetValue(governanceIndicator.Number, out var governanceIndicatorTemplate);

                return governanceIndicatorTemplate?
                    .AnswerCategories?
                    .Where(x => x.Text != governanceIndicator.AnswerCategory.Text)
                    .Select(x => new KeyValuePair<string, string>("", x.Text))
                    .ToList();
            }

            return GetTickedboxes(governanceIndicator.CheckBoxes, false);
        }

        public static List<KeyValuePair<string, string>> GetTickedboxes(IEnumerable<SharedEntities.CorporateGovernanceResearch.CheckBox> entities, bool isChecked)
        {
            return entities
                .Where(x => x.IsChecked?.Value == isChecked)
                .Select(entity => new KeyValuePair<string, string>(entity.TickboxCode, entity.Text))
                .ToList();
        }

        private class IndicatorSource
        {
            public string Text { get; set; }
            public string IndicatorsNumber { get; set; }
        }
    }

    public static class GovernanceIndicatorExtensions
    {
        public static bool IsPillar6(this SharedEntities.CorporateGovernanceResearch.GovernanceIndicator governanceIndicator)
            => governanceIndicator.Number.StartsWith("CG.6", StringComparison.Ordinal);
    }
}
